namespace FIREFLYX {
    /** 后处理效果  景深 DOF */
    export class PostEffectDepthOfField implements IExample {
        public Init(canvas: HTMLCanvasElement): void {
            console.log("PostEffectMotionBlur");
            let gl = canvas.getContext("webgl2");
            //app init
            App.init(gl.canvas as any);
            //简单观察相机
            let cVEnv = GenCamViewEnv();
            let cam = cVEnv.camera;
            let camHover = cVEnv.camHover;
            camHover.tiltAngle = 70;
            camHover.distance = 10;
            //添加 后处理效果 组件
            let depthOfField = cam.entity.addComponent<DepthOfField>(DepthOfField);
            depthOfField.setDOF(0.994);
            //灯光
            const light = GenMainLight(45);

            //模型节点
            let modles: GameObject[] = [];
            //中心圆柱体
            let cylinder = GameObjectUtil.createPrimitive(GameObjectPrimitiveType.CYLINDER);
            cylinder.transform.scale = new Vector3(2, 2, 2);
            modles.push(cylinder);
            //matrix 整列
            let size = 5;
            let halfS = size / 2;
            let gap = 8;
            for (let y = 0; y < size; y++) {
                for (let x = 0; x < size; x++) {
                    // let cube = new GameObject(`unit_${x}_${y}`);
                    let cube = GameObjectUtil.createPrimitive(GameObjectPrimitiveType.CYLINDER);
                    cube.transform.position = new Vector3((x - halfS) * gap, 0, (y - halfS) * gap);
                    cube.transform.scale = new Vector3(2, 2, 2);
                    modles.push(cube);
                }
            }
        }
    }

    /** 实现 DepthOfField 效果的脚本组件 */
    class DepthOfField extends Script {
        private _mtr: Material;
        private focalDistRate: number;
        private blurGap: number;
        private blurSigma: number;
        private blurLayer: number;
        private mainTexTexSize: Float32Array = new Float32Array(4);

        private makeShader() {
            //后处理 顶点程序
            let postEffVS = `#version 300 es
                precision highp float;
                precision highp int;
                layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
                layout (location = 3) in vec2 a_UV;       //UV 纹理坐标
                out vec2 v_uv;
                void main() {
                    v_uv = a_UV;
                    vec4 pos = vec4(a_Position.xyz , 1.0);
                    gl_Position = pos;
                }
            `;

            //后处理 片源程序
            let postEffFS = `#version 300 es
                precision highp float;
                precision highp int;
                uniform sampler2D u_mainTex;
                uniform sampler2D u_cameraDepthTexture;
                uniform float u_focalDistRate;          //焦距 0 - 1
                uniform float u_blurGap;                //卷积每层间隔单位
                uniform float u_blurSigma;              //二维正太分布中的西格玛值
                uniform float u_blurLayer;              //卷积层数
                uniform vec4 u_mainTexTexSize;         //main纹理的尺寸
                in vec2 v_uv;
                out vec4 color;
                const float pi = 3.1415926535898;
                const float max_Num = 100.0;

                float getGausWeight(float rx, float ry)
                {
                    float xDistance = float(rx*rx);
                    float yDistance = float(ry*ry);
                    float sigma22 = 2.0*u_blurSigma*u_blurSigma;
                    float sigma22PI = pi*sigma22;
                    return exp(-(xDistance + yDistance)/sigma22)/sigma22PI;
                }

                vec4 calcGausBlurColor(){
                    float size =2.0 * u_blurLayer + 1.0;
                    vec4 _color;
                    vec4 _sample;
                    float tx;
                    float ty;
                    float rx;
                    float ry;
                    float sum;
                    float tempWeight;
                    for(float i=0.0 ; i<max_Num ; i +=1.0){
                        if(i >=size) {break;}
                        for(float j=0.0 ; j<max_Num ; j +=1.0){
                            if(j >=size) {break;}
                            rx = -u_blurLayer + i;
                            ry = -u_blurLayer + j;
                            tx = rx * u_mainTexTexSize.x * u_blurGap;
                            ty = ry * u_mainTexTexSize.y * u_blurGap;
                            tempWeight = getGausWeight(rx,ry);
                            _sample = texture(u_mainTex,vec2(v_uv.x + tx ,v_uv.y + ty));
                            _color +=_sample * tempWeight;
                            sum += tempWeight;
                        }
                    }

                    return _color/sum;
                }

                void main(){
                    vec4 texColor = texture(u_mainTex , v_uv);                      //color纹理
                    vec4 texDepth = texture(u_cameraDepthTexture , v_uv);                     //深度纹理
                    //算模糊
                    vec4 blurColor = calcGausBlurColor();

                    //展示 深度
                    vec3 Depth = vec3(1.0 - texDepth.r) * 500.;                     //500 缩放系数是为了方便观察
                    float sw = step(u_focalDistRate , texDepth.r);

                    //最终色
                    // color = vec4(Depth , 1.0);                                   //深度
                    // color = blurColor;                                           //模糊
                    // color = (1.0 - sw) * texColor + sw * vec4(1.0,0.,0.,1.0);    //分界

                    color = (1.0 - sw) * texColor  + sw * blurColor;
                }
            `;

            //glsl
            const vs = new GLSL();
            vs.setCode(postEffVS);
            const fs = new GLSL();
            fs.setCode(postEffFS);

            //shader
            let result: Shader = new Shader();
            result.lightMode = RenderPipeline.LM_FORWARD;
            result.vertexGLSL = vs;
            result.fragmentGLSL = fs;
            //unifrom 默认值
            result.definePropertyMap = {};

            result.definePropertyMap.u_mainTex = {
                type: "texture",
                value: "White",
            };
            result.definePropertyMap.u_cameraDepthTexture = {
                type: "texture",
                value: "White",
            };

            //apply
            result.apply();

            return result;
        }

        private setMaterial() {
            if (!this._mtr) return;
            this._mtr.setProperty("u_focalDistRate", this.focalDistRate);
            this._mtr.setProperty("u_blurGap", this.blurGap);
            this._mtr.setProperty("u_blurSigma", this.blurSigma);
            this._mtr.setProperty("u_blurLayer", this.blurLayer);
            this._mtr.setProperty("u_mainTexTexSize", this.mainTexTexSize);
        }

        /**
         * 设置 DOF
         * @param focalDistRate 焦距 0 - 1
         * @param blurGap 卷积每层间隔单位
         * @param blurSigma 二维正太分布中的西格玛值
         * @param blurLayer 卷积层数
         */
        public setDOF(focalDistRate = 0.993, blurGap = 2, blurSigma = 6, blurLayer = 10) {
            let gl = App.webgl2;
            let cW = gl.canvas.width;
            let cH = gl.canvas.height;
            this.focalDistRate = focalDistRate;
            this.blurGap = blurGap;
            this.blurSigma = blurSigma;
            this.blurLayer = blurLayer;
            this.mainTexTexSize[0] = 1 / cW;
            this.mainTexTexSize[1] = 1 / cH;
            this.mainTexTexSize[2] = cW;
            this.mainTexTexSize[3] = cH;
            this.setMaterial();
        }

        public start() {
            let sh = this.makeShader();
            this._mtr = new Material("DepthOfField");
            this._mtr.shader = sh;
            this.setMaterial();
        }

        public onRenderImage(src: RenderTexture2D, dest: RenderTexture2D) {
            //渲染
            const mtr = this._mtr;
            Graphics.Blit(src, dest, mtr);  //直接从src 绘制到 dest
        }
    }
}